/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package submarines.ui;

import SubmarinesBindings.Boards;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedList;
import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import submarines.Board;
import submarines.Config;
import submarines.EaPlayer;
import submarines.HumanPlayer;
import submarines.Main.quitAction;
import submarines.Player;
import submarines.PlayerManager;
import submarines.TurnManager.turnAction;
import submarines.enemyBoard;
import com.google.gson.Gson;
import java.io.File;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletContext;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import submarines.thread.ClassExecutingTask;
import submarines.OnlineHumanPlayer;
import submarines.notices.NotficationManager;
import org.apache.commons.io.*;
import org.apache.tomcat.util.http.fileupload.FileItem;
import org.apache.tomcat.util.http.fileupload.disk.DiskFileItemFactory;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import submarines.*;
import submarines.thread.EaPlayerTurn;


    // <editor-fold defaultstate="collapsed" desc="HttpServlet methods. Click on the + sign on the left to edit the code.">
    /**
 *
 * @author shiran and elad
 */
@WebServlet(name = "actionManager", urlPatterns = {"/WebUi"})
public class WebUi extends HttpServlet implements Ui {

    submarines.PlayerManager pManager;
    Gson gsonReply;
   public String[][] subString;
   submarines.notices.NotficationManager nManager;
   ClassExecutingTask executingTask;
   EaPlayerTurn pTurn;
   
   public WebUi() {
        super();
        gsonReply = new Gson(); //use Gson reply stracture
        pManager = new PlayerManager();//create new game
        nManager = new NotficationManager(pManager);
        //executingTask = new ClassExecutingTask(pManager);
        //executingTask.start();
        pTurn = new EaPlayerTurn(pManager, nManager);
    }

    /** 
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code> methods.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException, NumberFormatException {
        PrintWriter out = response.getWriter();
        try {
            PlayerManager tmp;
            tmp = (PlayerManager) getServletContext().getAttribute("pManager");
            if (tmp == null) {
                pManager = new PlayerManager();//create new game
                nManager = new NotficationManager(pManager);
                pTurn = new EaPlayerTurn(pManager, nManager);
            }
            getServletContext().setAttribute("pManager", pManager);//share with WebUiGame
            getServletContext().setAttribute("nManager", nManager);//share with WebUiGame
            getServletContext().setAttribute("eaTurn", pTurn);//share with WebUiGame
            OnlineHumanPlayer p = pManager.getPlayerBySession(request.getSession().getId().toString());
            Reply status = new Reply();
            String action = request.getParameter("actionName"); //get request 

            //check what is the request and response
            if ("setnumplayers".equals(action.toLowerCase())) {
                if (pManager.getMaxPlayers() == -1) { //check if it is the first time that user enter players number
                    String numHumanPlayers = request.getParameter("humanNum"); //get number of human players 
                    String numComputerPlayers = request.getParameter("computerNum"); //get number of computer players 
                    int numHumanPlayersInt = Integer.parseInt(numHumanPlayers);
                    int numComputerPlayersInt = Integer.parseInt(numComputerPlayers);
                    int sumPlayers = numComputerPlayersInt + numHumanPlayersInt;
                    try {
                        pManager.setMaxPlayers(sumPlayers); //set max players in game
                    } catch (NumberFormatException e) {
                    }
                    addComputerPlayers(numComputerPlayersInt);
                    status.setReply("OK", null, "nextPlayer", "Max players was set"); //response the max players was set
                } else {
                    status.setReply("ERROR", null, "nextMove", "Max players was already set"); //an error occured 
                }
            } else if ("addplayer".equals(action.toLowerCase())) {
                String playerName = request.getParameter("playerName"); //get parameters from the request
                String playerType = request.getParameter("playerType");//get parameters from the request
                if (playerName != null && playerType != null && !playerName.isEmpty() && !playerType.isEmpty()) {
                    status = createNewPlayer(playerName, playerType, request); //create new player
                } else {
                    status.setReply("ERROR", null, "returnPlayer", "One of the requested parameters is missing");
                } 
            } else if ("removeplayer".equals(action.toLowerCase())) {     
                String playerName = request.getParameter("playerSession"); //get parameters from the request
                if (playerName != null && !playerName.isEmpty() ) {
                    status = removePlayer(playerName, request); //create new player
                } else {
                    status.setReply("ERROR", null, "returnPlayer", "One of the requested parameters is missing");
                }
            } else if ("addship".equals(action.toLowerCase())) { 
                //get all parameters from request
                p.setSessionTime();
                String playerName = request.getParameter("playerName");
                String sRow = request.getParameter("row");
                String sCol = request.getParameter("col");
                String sDirection = request.getParameter("dir");
                String sSize = request.getParameter("size");

                if (playerName != null && sRow != null && sCol != null && sDirection != null && sSize != null && !playerName.isEmpty() && !sRow.isEmpty() && !sCol.isEmpty() && !sDirection.isEmpty() && !sSize.isEmpty()) {
                    int size = getSubSize(sSize); //check sho size
                    if (checkShipSize(size, playerName)) { //check if playe can add this ship
                        int row = Integer.parseInt(sRow);
                        int col = Integer.parseInt(sCol);
                        int direction = getDirection(sDirection);
                        Player playerByName = pManager.getPlayerByName(playerName);
                        status = insertSub(playerByName, row, col, size, direction); //insert ship
                    } else {
                        status.setReply("ERROR", null, "returnShip", "There is no more ships to insert with size:" + size);
                    }
                } else {
                    status.setReply("ERROR", null, "returnShip", "One of the requested parameters is missing");
                }
            } else if ("saveboard".equals(action.toLowerCase())) { //save board cheks that all submarines are on board before next player
                p.setSessionTime();
                String playerName = request.getParameter("playerName");
                if (playerName != null && !playerName.isEmpty()) {
                    status = checkBoard(playerName, request);
                }else{
                     status.setReply("ERROR", null, "returnBoard", "One of the requested parameters is missing");
                }
            } else if ("removeship".equals(action.toLowerCase())) { 
                p.setSessionTime();
                //get all parametes from request
                String playerName = request.getParameter("playerName");
                String sRow = request.getParameter("row");
                String sCol = request.getParameter("col");
                //check parameters values are ok
                if (playerName != null && sRow != null && sCol != null && !playerName.isEmpty() && !sRow.isEmpty() && !sCol.isEmpty()) {
                    int row = Integer.parseInt(sRow);
                    int col = Integer.parseInt(sCol);
                    Player playerByName = pManager.getPlayerByName(playerName);
                    status = removeSub(playerByName, row, col); //remove ship
                } else {
                    status.setReply("ERROR", null, "returnShip", "One of the requested parameters is missing");
                }
            } else if ("getsubstatus".equals(action.toLowerCase())) { //check status of submarines to display subs that player still need to put
                String playerName = request.getParameter("playerName");
                if (playerName != null && !playerName.isEmpty()) {
                    status = GetSubsStatus(playerName, request);
                } else {
                    status.setReply("ERROR", null, "returnStatus", "Player name parameter is missing");
                }
            }  else if ("isplayersready".equals(action.toLowerCase())) { //check status of submarines to display subs that player still need to put
                p.setSessionTime();
                status = checkPlayersReadiness();
            } else if ("uploadxml".equals(action.toLowerCase())) {
                File file ;
                int maxFileSize = 5000 * 1024;
                int maxMemSize = 5000 * 1024;
                ServletContext context = getServletContext();
                
                String filePath;
                String pathSeperator;
                if (Config.OS.equals("linux")) {
                    filePath = "/tmp";
                    pathSeperator = "/";
                } else {
                    filePath = "c:\\temp";
                    pathSeperator = "\\";
                }

                // Verify the content type
                String contentType = request.getContentType();
                if ((contentType.indexOf("multipart/form-data") >= 0)) {

                    DiskFileItemFactory factory = new DiskFileItemFactory();
                    // maximum size that will be stored in memory
                    factory.setSizeThreshold(maxMemSize);
                    // Location to save data that is larger than maxMemSize.
                    factory.setRepository(new File(filePath));

                    // Create a new file upload handler
                    ServletFileUpload upload = new ServletFileUpload(factory);
                    // maximum file size to be uploaded.
                    upload.setSizeMax( maxFileSize );
                    try{ 
                        // Parse the request to get file items.
                        List fileItems = upload.parseRequest(request);

                        // Process the uploaded file items
                        Iterator i = fileItems.iterator();
                        while ( i.hasNext () ) 
                        {
                            FileItem fi = (FileItem)i.next();
                            if ( !fi.isFormField () )	
                            {
                            // Get the uploaded file parameters
                            String fieldName = fi.getFieldName();
                            String fileName = fi.getName();
                            boolean isInMemory = fi.isInMemory();
                            long sizeInBytes = fi.getSize();
                            // Write the file
                            if( fileName.lastIndexOf(pathSeperator) >= 0 ){
                            file = new File( filePath + 
                            fileName.substring( fileName.lastIndexOf(pathSeperator))) ;
                            }else{
                            file = new File( filePath + "" + pathSeperator + "" + fileName.substring(fileName.lastIndexOf(pathSeperator)+1)) ;
                            fi.write( file ) ;
                            }
                            testParseEntireDocument(filePath + "" + pathSeperator + "" + fileName);
                            boolean success = file.delete();
                            if (!success){
                                status.setReply("ERROR", null, "returnXml", "Could not delete file");
                            }
                            status.setReply("OK", null, "xmlok", "");
                            }
                        }
                    }catch(Exception ex) {
                        status.setReply("ERROR", null, "returnXml", "Exception: " + ex.getMessage());
                    }
                }else{
                    status.setReply("ERROR", null, "returnXml", "content type not multipart/form-data");
                }
            } else if ("pickplayer".equals(action.toLowerCase())) { //get the players that are still in the game
                String currPlayer = request.getParameter("playerName");
                if (currPlayer != null && !currPlayer.isEmpty()) {
                    status = setSessionsForAllPlayers(currPlayer, request);
                } else{
                    status.setReply("ERROR", null, "returnPickPlayer", "One of the requested parameters is missing");
                }
            }

            String json = new String();
            json = gsonReply.toJson(status); //prepare Gson response
            response.setContentType("text/html;charset=UTF-8");
            out.println(json); //send response
        } finally {
            out.close();
        }
    }
/** 
     * Handles the HTTP <code>GET</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /** 
     * Handles the HTTP <code>POST</code> method.
     * @param request servlet request
     * @param response servlet response
     * @throws ServletException if a servlet-specific error occurs
     * @throws IOException if an I/O error occurs
     */
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    /** 
     * Returns a short description of the servlet.
     * @return a String containing servlet description
     */
    @Override
    public String getServletInfo() {
        return "Short description";
    }// </editor-fold>

    private Reply createNewPlayer(String name, String type, HttpServletRequest request) {
        subString = Config.cloneAliases(); //list of submarines to check subamarine status for each player
        if (type.toUpperCase().equals(playerType.HUMAN_PLAYER.toString())) {
            if (pManager.getPlayerBySession(request.getSession().getId().toString()) != null) {//there is a player with the same name
            //if (pManager.getPlayerByName(name) != null) {//there is a player with the same name
                Reply res = new Reply("ERROR", null, "returnPlayer", "There is already a user with the same session");
                return res;
            }
            OnlineHumanPlayer humanPlayer = new OnlineHumanPlayer(name);
            humanPlayer.setSession(request);
            humanPlayer.setSessionTime();
            if (humanPlayer != null) { 
                if (!pManager.addPlayerRandom(humanPlayer)) {
                    Reply res = new Reply("ERROR", null, "getBoard", "Could not add player there is max players was added");//check that players didn't passmax players number
                }
                Reply res = new Reply("OK", humanPlayer.myBoard.gameBoard, "getBoard", ""); //return player board in response
                return res;
            }
            Reply res = new Reply("ERROR", null, "returnPlayer", "Error creating player");//player was not create
        } else {//eaPlayer
            EaPlayer eaPlayer = new EaPlayer(name);
            if (eaPlayer != null) {
                pManager.addPlayer(eaPlayer);
                eaPlayer.fillBoard();
            }
            Reply res = new Reply("ERROR", null, "returnPlayer", "Error creating player"); //player was not created
        }
        if(pManager.checkPlayers())
        {
            //nManager = new NotficationManager(pManager);
            //getServletContext().setAttribute("nManager", (Object) nManager);//share new game with WebUi to create game

            pManager.setCompetitorBoards();
            executingTask.cancel();
            Reply res = new Reply("OK", null, "startGame", "");
            return res;
        }
        else
        {
            Reply res = new Reply("OK", null, "Wait", "");
            return res;
        }
    }
    
    private void addComputerPlayers(int num) {
        for(int i = 0; i<num; i++)
        {
            OnlineEaPlayer eaPlayer = new OnlineEaPlayer("Player" + (i+1), pManager);
            if (eaPlayer != null) {
                if (!pManager.addPlayer(eaPlayer)) {
                    Reply res = new Reply("ERROR", null, "getBoard", "Could not add player there is max players was added");//check that players didn't passmax players number
                }
                //pManager.addPlayer(eaPlayer);
                eaPlayer.fillBoard();
            }
        }
    }
    
    private Reply insertSub(Player name, int row, int col, int shipSize, int direction) {
        if (name.myBoard.insertShip(row, col, shipSize, direction)) { //insert new ship
                Reply res = new Reply("OK", name.myBoard.gameBoard, "nextShip", ""); //return board game in respons
                return res;
            //}
        } else {
            Reply res = new Reply("ERROR", name.myBoard.gameBoard, "returnShip", "Ship location in not valid");
            return res;
        }
    }

    private Reply checkBoard( String currPlayerName, HttpServletRequest request) { //when save boar check that all submarines is on board
        Player currPlayer = pManager.getPlayerBySession(request.getSession().getId().toString()); //get current player to check board
        if (currPlayer.myBoard.subsLeft != Config.SUBMARINES.length) { //if there is less submarines then needed player can't press next
            Reply res = new Reply("ERROR", null, "returnBoard", "Not all submatines are on the board");
            return res;
        } else {
            if(pManager.checkPlayers())
            {
                //nManager = new NotficationManager(pManager);
                //getServletContext().setAttribute("nManager", (Object) nManager);//share new game with WebUi to create game

                pManager.setCompetitorBoards();
               // executingTask.cancel();
                Reply res = new Reply("OK", null, "startGame", "");
                return res;
            }
            else
            {
                Reply res = new Reply("OK", null, "Wait", "");
                return res;
            }
        }
    }

    private boolean checkShipSize(int size, String currPlayer) { //check if player has ship in the given size to put on board or if size is invalid
        Player playerByName = pManager.getPlayerByName(currPlayer);
        int[] remainingSubs = playerByName.myBoard.getRemainingSubs();
        if (remainingSubs[size - 1] == 0 || size == -1) {
            return false;
        } else {
            return true;
        }
    }

    private int getSubSize(String sSize) { //return submarine size according to given submarine name
        if ("patrol_boat".equals(sSize.toLowerCase())) {
            return Config.PATROL_BOAT;
        } else if ("aircraft_carrier".equals(sSize.toLowerCase())) {
            return Config.AIRCRAFT_CARRIER;
        } else if ("battleship".equals(sSize.toLowerCase())) {
            return Config.BATTLESHIP;
        } else if ("destroyer".equals(sSize.toLowerCase())) {
            return Config.DESTROYER;
        } else if ("submarine".equals(sSize.toLowerCase())) {
            return Config.SUBMARINE;
        }
        return -1;
    }

    private int getDirection(String sDirection) { //get submarine direction from String to int
        if ("horizontal".equals(sDirection) || "horizontal".equals(sDirection.toLowerCase()) || "horizontal".equals(sDirection.toUpperCase())) {
            return Config.HORIZONTAL;
        } else {
            return Config.VERTICAL;
        }
    }

    private Reply GetSubsStatus(String currPlayer, HttpServletRequest request) { //return the submarines status to display to user
        Player playerByName = pManager.getPlayerBySession(request.getSession().getId().toString());
        //subString = Config.ALIASES.clone();
        int[] initial = Config.SUBMARINES.clone();
        
        for (int i = 0; i < playerByName.myBoard.subs.length; i++) {
            for (int j = playerByName.myBoard.subsToCreate[i]; j < initial[i]; j++) {
                subString[i][j] = ""; //ckear sub from list if player put it already
            }
        }
        Reply res = new Reply("OK", subString, "setSubStatus", "");
        return res;
    }

    private Reply removeSub(Player playerByName, int row, int col) { //remove submarine from current player board
        int size = playerByName.myBoard.getSubSize(row, col);
        if(size == 3) //handle the 2 subs with the same name if player removed insert ship again to sub status list
        {
            if("".equals(subString[size - 1][0]))
            {
                subString[size - 1][0] = Config.ALIASES[size - 1][0];
            }
            else
            {
                subString[size - 1][1] = Config.ALIASES[size - 1][1];
            }
        }
        else
        {
            subString[size - 1][0] = Config.ALIASES[size - 1][0]; //insert removed ship to status list
        }
        if (playerByName.myBoard.removeShip(row, col)) {
            
            Reply res = new Reply("OK", playerByName.myBoard.gameBoard, "nextShip", "");
            return res;
        }
        Reply res = new Reply("ERROR", null, "returnRemove", "");
        return res;
    }
    
    private Reply checkPlayersReadiness() {
        if(pManager.checkPlayers())
        {
            //executingTask.cancel();
            Reply res = new Reply("OK", null, "startGame", "");
            return res;
        }
        else
        {
            Reply res = new Reply("OK", null, "Wait", "");
            return res;
        }
    }
    
    private Reply removePlayer(String session, HttpServletRequest request) {
        OnlineHumanPlayer player = pManager.getPlayerBySession(session);
        if (player == null) {//there is a player with the same name
                Reply res = new Reply("ERROR", null, "returnPlayer", "There is no such session");
                return res;
            }
        pManager.removePlayer(player);
        Reply res = new Reply("OK", null, "playerRemoved", "player was removed");
        return res;
    }
    
        
    public final void testParseEntireDocument(String path) throws JAXBException { //use for web
        JAXBContext jc = JAXBContext.newInstance(Boards.class.getPackage().getName()); 
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        SubmarinesBindings.Submarines sub = new SubmarinesBindings.Submarines();
        sub = (SubmarinesBindings.Submarines) unmarshaller.unmarshal(new File(path));
        List<SubmarinesBindings.Player> player = (List<SubmarinesBindings.Player>) sub.getPlayers().getPlayer();
        List<SubmarinesBindings.Boat> boats = (List<SubmarinesBindings.Boat>) sub.getBoats().getBoat();
        List<SubmarinesBindings.Board> boards = (List<SubmarinesBindings.Board>) sub.getBoards().getBoard();
        
        pManager = new PlayerManager(player, boats, boards);
        getServletContext().setAttribute("pManager", pManager);
        nManager = new NotficationManager(pManager);
        getServletContext().setAttribute("nManager",  nManager);//share new game with WebUi to create game
        pTurn = new EaPlayerTurn(pManager, nManager);
        getServletContext().setAttribute("eaTurn", pTurn);
    }
    
    private Reply setSessionsForAllPlayers(String player, HttpServletRequest request) {
        submarines.OnlineHumanPlayer currPlayer = (submarines.OnlineHumanPlayer) pManager.getPlayerByName(player);
        if(currPlayer.getSessionId() == null)
        {
            currPlayer.setSession(request);
            Reply res = new Reply("OK", null, "Wait", "session was set");
            return res;
        }
        Reply res = new Reply("ERROR", null, "returnPickPlayer", "session already exist");
        return res;
    }
    
    //public boolean cleanBoard(Player name) {
   //     name.myBoard.cleanBoard();
   //     return true;
    //}

    @Override
    public String getMissleCoords() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public quitAction getNewGameAswer() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int getSubmarineDirection(int subSize) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int getSubmarineRow(int subSize, int minRow, int maxRow) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int getSubmarineCol(int subSize, int minCol, int maxCol) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public turnAction getPlayersNextMove() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public String getTargetedPlayer(LinkedList<String> list) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public int getNumberOfPlayers() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public playerType getPlayerType() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public String getPlayerName() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public boolean addAnotherPlayer() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void printAlert(String msg) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void printBoard(Board toPrint) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void clearScreen() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void printEnemyBoard(enemyBoard board) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public String askUserYesNoQuestions(String question) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void printComputerMove(String attacker, enemyBoard board, int row, int col) {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}
